﻿/*
 * SpatialDbCommand
 * 
 * initial implementation : 20.9.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Text.RegularExpressions;
//using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Windows.Markup;
using Petra.Gis.Geometry.Support;
using Petra.Gis.DataServices.Metadata;
using Petra.Gis.ReferenceSystemServices;


namespace Petra.Gis.DataServices
{

  public abstract partial class SpatialDbCommand : DbCommand
  {

    protected string _commandText;
    //protected SpatialDbConnection _conection;
    
    //private SqlTransaction _trans;
    //public Dictionary<string, object> Extensions = new Dictionary<string, object>();

    // Implement the default constructor here.
    public SpatialDbCommand()
    {
      // spatial command has 1 default parameter
      // coarse spatial filter

    }



    // Implement other constructors here.
    public SpatialDbCommand(string cmdText)
      : this()
    {
      _commandText = cmdText;
    }





    public SpatialDbCommand(string cmdText, SpatialDbConnection connection)
      : this(cmdText)
    {
      this.Connection = connection;
    }





    public bool EqualParams(SpatialDbCommand anotherCmd)
    {
      if (!(anotherCmd is SpatialDbCommand))
        return false;

      return this.GetType() == anotherCmd.GetType() &&
             this.CommandText == anotherCmd.CommandText &&
             ((SpatialDbConnection)this.Connection).EqualParams((SpatialDbConnection)anotherCmd.Connection) &&
             this.Parameters.Count == anotherCmd.Parameters.Count;
    }






    #region command validation


    protected virtual void validateBeforeRun()
    {

      recoverConnectionFromXState();

      // There must be a valid and open connection.
      if (this.Connection == null || this.Connection.State != ConnectionState.Open)
        throw new InvalidOperationException("Connection must valid and open");

      //command text must not be empty when run
      if (this.CommandText == null || this.CommandText == "")
        throw new InvalidOperationException("command text must be supplied");

      //lookup for spatial metadata
      //getSpatialMetadata();

    }





    protected void recoverConnectionFromXState()
    {
      // if connection is recovered from xaml it might still stay closed
      // but xstate shows its state before xaml save
      // so if was open then reopen now
      if (((SpatialDbConnection)this.Connection).XState == ConnectionState.Open &&
          this.Connection.ConnectionString != "" &&
          this.Connection.State != ConnectionState.Open)
        this.Connection.Open();
    }






    //protected void getSpatialMetadataFromSqlText(string cmdText)
    //{
    //  // parse SQL using regex... try get FROM table_name
    //  // parses table name as following:
    //  // schema..table, schema.owner.table, owner.table, table
    //  string pattern = @"(?<from>(F|f)(R|r)(O|o)(M|m))\s*(" +
    //    @"(\[?(?<schemaname>\w+)\]?\.\[?(?<ownername>\w+)\]?\.\[?(?<tablename>\w+)\]?)|" +
    //    @"(\[?(?<schemaname>\w+)\]?\.\.\[?(?<tablename>\w+)\]?)|" +
    //    @"(\[?(?<ownername>\w+)\]?\.\[?(?<tablename>\w+)\]?)|" +
    //    @"(\[?(?<tablename>\w+)\]?))+";

    //  Regex r = new Regex(pattern);
    //  Match m = r.Match(cmdText);

    //  if (m.Length < 1)
    //    //throw new ArgumentException("non readable command");
    //    return;

    //  // must be only one occurrence of from
    //  // if multiple sql can be join or other complex query
    //  else if (m.Groups["from"].Captures.Count == 1)
    //  {
    //    string schema = (m.Groups["schemaname"].Captures.Count == 1 ? m.Groups["schemaname"].Captures[0].Value : null);
    //    string owner = (m.Groups["ownername"].Captures.Count == 1 ? m.Groups["ownername"].Captures[0].Value : null);
    //    string table = m.Groups["tablename"].Captures[0].Value;

    //    getSpatialMetadataFromTableName(schema, owner, table);
    //  }

    //}




    //    protected void getSpatialMetadataFromRemark(string cmdText)
    //    {
    //      //try search metadata in remark...
    //      // /*UseSpatialMetadataFrom[FeatureSchema=bla;FeatureOwner=bla;FeatureName=BlaBla]*/
    //      string pattern = @"(?<sptlremark>[\/*]|[\-\-]\s*UseSpatialMetadataFrom\[
    //          (?<schema>((FeatureSchema\=)(?<schemaname>([a-z]{1}|[A-Z]{1}\w*)\;)?)
    //          (?<owner>((FeatureOwner\=)(?<ownername>([a-z]{1}|[A-Z]{1}\w*)\;)?)
    //          (?<table>((FeatureName\=)(?<tablename>([a-z]{1}|[A-Z]{1}\w*)))
    //          )";
    //      Regex r = new Regex(pattern);
    //      Match m = r.Match(cmdText);

    //      if (m.Length < 1)
    //        //throw new ArgumentException("non readable command");
    //        return;
    //      else if (m.Groups["sptlremark"].Captures.Count == 1)
    //      {

    //        string schema = (m.Groups["schemaname"].Captures.Count == 1 ? m.Groups["schemaname"].Captures[0].Value : "");
    //        string owner = (m.Groups["ownername"].Captures.Count == 1 ? m.Groups["ownername"].Captures[0].Value : "");
    //        string table = m.Groups["tablename"].Captures[0].Value;

    //        getSpatialMetadataFromTableName(schema, owner, table);
    //      }
    //    }


    #endregion command validation



    ///****
    // * IMPLEMENT THE REQUIRED PROPERTIES.
    // ****/

    //public override bool DesignTimeVisible
    //{
    //  get
    //  {
    //    throw new NotImplementedException();
    //  }
    //  set
    //  {
    //    throw new NotImplementedException();
    //  }
    //}



    //public override string CommandText
    //{
    //  get { return _sqlCmd.CommandText; }
    //  set { _sqlCmd.CommandText = value; }
    //}



    //public override int CommandTimeout
    //{
    //  /*
    //   * The sample does not support a command time-out. As a result,
    //   * for the get, zero is returned because zero indicates an indefinite
    //   * time-out period. For the set, throw an exception.
    //   */
    //  get { return _sqlCmd.CommandTimeout; }
    //  set { _sqlCmd.CommandTimeout = value; }
    //}



    //public override CommandType CommandType
    //{
    //  /*
    //   * The sample only supports CommandType.Text.
    //   */
    //  get { return _sqlCmd.CommandType; }
    //  set { _sqlCmd.CommandType = value; }
    //}



    //new public SpatialDbConnection Connection
    //{
    //  get { return _conection; }
    //  set
    //  {

    //    /*
    //     * The connection is associated with the transaction
    //     * so set the transaction object to return a null reference if the connection 
    //     * is reset.
    //     */
    //    if (this.Connection != value)
    //      this.Transaction = null;

    //    _conection = value;
    //  }
    //}






    /// <summary>
    /// this method can force SpatialDbCommand to load 'foreign' (other) feature metadata.
    /// Implementation of this functionality is optional and slaves typically to RDBMS 
    /// where 'on the fly' join query or stored procedure can be called, 
    /// where no metadata can be persist in database, <see cref="SpatialDbConnection.SpatialSchema"/>
    /// by calling this, command will get existing metadata.
    /// </summary>
    /// <param name="fullFeatureId"></param>
    //public virtual void ForceMetadataFrom(string fullFeatureName)
    //{
    //  getSpatialMetadataFromTableName(fullFeatureName);
    //}





    //protected Dictionary<string, BoundingBox> _coarseSpatialFilters;

    ///// <summary>
    ///// coarse spatial filter cant be processed as commandParameter directly
    ///// because it is complex type and must be translated before ExecuteReader
    ///// into parameters
    ///// </summary>
    //public virtual Dictionary<string, BoundingBox> CoarseSpatialFilters
    //{
    //  get { return _coarseSpatialFilters; }
    //  set { _coarseSpatialFilters = value; }
    //}





    /// <summary>
    /// helper method that takes BoundingBox and translate it into correct CommandParameters
    /// depending on data provider specifics. parameters must remain rather as simple types, not BoundingBox
    /// feature might have multiple geometries and this method enables to set spatial filter for all
    /// each BoundingBox must be in coordinate system of geometry field given by its name
    /// </summary>
    /// <param name="bounds"></param>
    //public abstract void PrepareCoarseSpatialFilterParameters(Dictionary<string, BoundingBox> bounds);





    //protected SpatialDbConnection _connection;

    //protected override DbConnection DbConnection
    //{
    //  get { return _connection; }
    //  set { _connection = (SpatialDbConnection)value; }
    //}



    //protected override DbConnection DbConnection
    //{
    //  get { return this.Connection; }
    //  set { this.Connection = (GdoSqlConnection)value; }
    //}



    //public DbParameterCollection Parameters
    //{
    //  get { return _sqlCmd.Parameters; }
    //}



    //protected override DbParameterCollection DbParameterCollection
    //{
    //  get { return this.Parameters; }
    //}



    //public DbTransaction Transaction
    //{
    //  /*
    //   * Set the transaction. Consider additional steps to ensure that the transaction
    //   * is compatible with the connection, because the two are usually linked.
    //   */
    //  get { return _trans; }
    //  set { _trans = (SqlTransaction)value; }
    //}



    //protected override DbTransaction DbTransaction
    //{
    //  /*
    //   * Set the transaction. Consider additional steps to ensure that the transaction
    //   * is compatible with the connection, because the two are usually linked.
    //   */
    //  get { return this.Transaction; }
    //  set { this.Transaction = value; }
    //}



    //public override UpdateRowSource UpdatedRowSource
    //{
    //  get { return _sqlCmd.UpdatedRowSource; }
    //  set { _sqlCmd.UpdatedRowSource = value; }
    //}



    ///****
    // * IMPLEMENT THE REQUIRED METHODS.
    // ****/
    //public override void Cancel()
    //{
    //  _sqlCmd.Cancel();
    //}





    //public DbParameter CreateParameter()
    //{
    //  return new SqlParameter();
    //}

    //protected override DbParameter CreateDbParameter()
    //{
    //  return CreateParameter();
    //}




    //public override int ExecuteNonQuery()
    //{
    //  /*
    //   * ExecuteNonQuery is intended for commands that do
    //   * not return results, instead returning only the number
    //   * of records affected.
    //   */

    //  // validate before run
    //  validateBeforeRun();

    //  // Execute the command.
    //  return _sqlCmd.ExecuteNonQuery();

    //}





    //public DbDataReader ExecuteReader()
    //{
    //  // validate before run
    //  validateBeforeRun();

    //  return getgdosqlDataReader(_sqlCmd.ExecuteReader());
    //}





    //public DbDataReader ExecuteReader(CommandBehavior behavior)
    //{
    //  // validate before run
    //  validateBeforeRun();

    //  return getgdosqlDataReader(_sqlCmd.ExecuteReader(behavior));
    //}





    //protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
    //{
    //  return ExecuteReader(behavior);
    //}

    //private SpatialDbDataReader getgdosqlDataReader(DbDataReader dr)
    //{

    //  SpatialDbDataReader retval = new SpatialDbDataReader(dr, _gFieldNames);
    //  return retval;
    //}





    //public override object ExecuteScalar()
    //{
    //  // validate before run
    //  validateBeforeRun();

    //  return _sqlCmd.ExecuteScalar();

    //}





    //public override void Prepare()
    //{
    //  // The sample Prepare is a no-op.
    //}





    //public void Dispose()
    //{
    //  this.Dispose(true);
    //  System.GC.SuppressFinalize(this);
    //}
    //





    //protected override void Dispose(bool disposing)
    //{
    //  /*
    //   * Dispose of the object and perform any cleanup.
    //   */
    //  base.Dispose();
    //}

  }
}
